package com.ywy.controller.pay;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.ywy.config.WechatPayConfig;
import com.ywy.entity.User;
import com.ywy.service.UserService;
import com.ywy.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.util.*;

/**
 * 微信支付Controller
 */
@Slf4j
@Controller
@RequestMapping("wechatPay")
public class WechatPayController {
    @Value("${system.vipMoney}")
    private String vipMoney;

    @Resource
    private WechatPayConfig wechatPayConfig;
    @Resource
    private UserService userService;

    /**
     * 支付请求
     *
     * @param request
     * @param response
     * @param session
     * @return
     * @throws ServletException
     * @throws Exception
     */
    @RequestMapping("pay")
    public ModelAndView pay(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        try {
            User user = (User) session.getAttribute("currentUser");
            String userName = user.getUsername(); // 获取用户名
            String orderNo = userName + "-" + DateUtil.formatDatePure(); // 生成订单号  用户名+日期格式

            String userAgent = request.getHeader("user-agent");
            ModelAndView mav = new ModelAndView();

            // 移动设备支付请求
            if (DeviceUtil.isMobileDevice(userAgent)) {
                Map<String, Object> map = new HashMap<>();
                map.put("appid", wechatPayConfig.getAppid());
                map.put("mch_id", wechatPayConfig.getMchId());
                map.put("device_info", wechatPayConfig.getDeviceInfo());
                map.put("notify_url", wechatPayConfig.getNotifyUrl());
                map.put("trade_type", "MWEB"); // 交易类型
                map.put("out_trade_no", orderNo);
                map.put("body", "尊贵资源共享平台VIP会员 你值得拥有！");
                map.put("total_fee", Integer.parseInt(vipMoney) * 100);
                map.put("nonce_str", StringUtil.getRandomString(30)); // 随机串
                map.put("spbill_create_ip", getRemoteIP(request)); // 终端IP
                map.put("sign", getSign(map)); // 签名
                String xml = XmlUtil.genXml(map);
                log.info("微信支付请求参数：{}", xml);
                HttpResponse httpResponse = HttpClientUtil.sendXMLDataByPost(wechatPayConfig.getUrl(), xml); // 发送请求
                log.info("微信支付响应结果：{}", EntityUtils.toString(httpResponse.getEntity()));
                InputStream in = httpResponse.getEntity().getContent(); // 发送xml消息
                String redirect = XmlUtil.getElementValue(in, "mweb_url");
                // 拼接跳转地址
                redirect += "&redirect_url=" + URLEncoder.encode(wechatPayConfig.getReturnUrl(), "GBK");
                log.info("微信支付请求redirect:" + redirect);
                response.sendRedirect(redirect);
            } else {
                mav.addObject("orderNo", orderNo);
                mav.addObject("vipMoney", vipMoney);
                mav.setViewName("vip/wechatPay");
                return mav;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 微信支付同步通知页面
     *
     * @return
     */
    @RequestMapping("payResult")
    public ModelAndView payResult() {
        log.info("微信支付服务器同步通知");
        ModelAndView mav = new ModelAndView();
        mav.addObject("message", "充值VIP成功，请重新登录生效，然后联系管理员（QQ:11223344），领取会员福利！");
        mav.setViewName("vip/payResult");
        return mav;
    }

    /**
     * 微信支付服务器异步通知
     *
     * @param request
     * @throws Exception
     */
    @RequestMapping("payNotify")
    public void payNotify(HttpServletRequest request) {
        try {
            InputStream is = request.getInputStream();
            StringBuffer sb = new StringBuffer();
            String s;
            BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            while ((s = reader.readLine()) != null) {
                sb.append(s);
            }
            reader.close();
            is.close();

            //解析xml成map
            Map<String, String> map = XmlUtil.parseToMap(sb.toString());

            SortedMap<Object, Object> requestParams = new TreeMap<>();
            Iterator<String> it = map.keySet().iterator();
            while (it.hasNext()) {
                String parameter = it.next();
                String parameterValue = map.get(parameter);

                if (null != parameterValue) {
                    parameterValue = parameterValue.trim();
                }
                log.info("p:" + parameter + ",v:" + parameterValue);
                requestParams.put(parameter, parameterValue);
            }
            log.info("微信支付服务器异步通知：{}", requestParams);

            // 微信支付的API密钥
            String key = wechatPayConfig.getKey();

            if (checkSign(requestParams, key)) { // 验证通过
                if ("SUCCESS".equals((String) requestParams.get("result_code"))) {
                    String out_trade_no = (String) requestParams.get("out_trade_no");
                    String username = out_trade_no.split("-")[0];
                    User user = userService.getByUsername(username);
                    user.setVip(true);
                    user.setPoints(1000000);
                    userService.save(user);
                }
            } else {
                log.error("验证未通过");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 生产支付二维码
     *
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping("loadPayImage")
    public void loadPayImage(HttpServletRequest request, HttpServletResponse response) {
        try {
            String orderNo = request.getParameter("orderNo");
            Map<String, Object> map = new HashMap<>();
            map.put("appid", wechatPayConfig.getAppid());
            map.put("mch_id", wechatPayConfig.getMchId());
            map.put("device_info", wechatPayConfig.getDeviceInfo());
            map.put("notify_url", wechatPayConfig.getNotifyUrl());
            map.put("trade_type", "NATIVE"); // 交易类型
            map.put("out_trade_no", orderNo);
            map.put("body", "尊贵资源共享平台VIP会员 你值得拥有！");
            map.put("total_fee", Integer.parseInt(vipMoney) * 100);
            map.put("nonce_str", StringUtil.getRandomString(30)); // 随机串
            map.put("spbill_create_ip", getRemoteIP(request)); // 终端IP
            map.put("sign", getSign(map)); // 签名
            String xml = XmlUtil.genXml(map);
            log.info("微信支付请求参数：{}", xml);
            HttpResponse httpResponse = HttpClientUtil.sendXMLDataByPost(wechatPayConfig.getUrl(), xml);// 发送请求
            log.info("微信支付响应结果：{}", EntityUtils.toString(httpResponse.getEntity()));
            InputStream in = httpResponse.getEntity().getContent();
            String codeUrl = XmlUtil.getElementValue(in, "code_url");
            log.info("微信支付请求codeUrl:" + codeUrl);
            MultiFormatWriter multiFormatWriter = new MultiFormatWriter();

            Map hints = new HashMap();
            BitMatrix bitMatrix = multiFormatWriter.encode(codeUrl, BarcodeFormat.QR_CODE, 250, 250, hints);
            BufferedImage image = toBufferedImage(bitMatrix);
            //输出二维码图片流
            ImageIO.write(image, "png", response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询订单支付状态
     *
     * @param orderNo
     * @return
     */
    @ResponseBody
    @RequestMapping("queryPayState")
    public Boolean queryPayState(String orderNo) {
        String username = orderNo.split("-")[0];
        return userService.getByUsername(username).isVip();
    }

    /**
     * 获取本机IP地址
     *
     * @return IP
     */
    private static String getRemoteIP(HttpServletRequest request) {
        if (request.getHeader("x-forwarded-for") == null) {
            return request.getRemoteAddr();
        }
        return request.getHeader("x-forwarded-for");
    }

    /**
     * 微信支付签名算法sign
     */
    private String getSign(Map<String, Object> map) {
        StringBuffer sb = new StringBuffer();
        String[] keyArr = (String[]) map.keySet().toArray(new String[map.keySet().size()]);//获取map中的key转为array
        Arrays.sort(keyArr);//对array排序
        for (int i = 0, size = keyArr.length; i < size; ++i) {
            if ("sign".equals(keyArr[i])) {
                continue;
            }
            sb.append(keyArr[i] + "=" + map.get(keyArr[i]) + "&");
        }
        sb.append("key=" + wechatPayConfig.getKey());
        return MD5Util.md5(sb.toString());
    }

    /**
     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     *
     * @return boolean
     */
    private static boolean checkSign(SortedMap<Object, Object> packageParams, String API_KEY) {
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + API_KEY);

        //算出摘要
        String mySign = MD5Util.md5(sb.toString()).toLowerCase();
        String sign = ((String) packageParams.get("sign")).toLowerCase();

        return sign.equals(mySign);
    }

    /**
     * 类型转换
     *
     * @param matrix
     * @return
     * @author chenp
     */
    private static BufferedImage toBufferedImage(BitMatrix matrix) {
        int width = matrix.getWidth();
        int height = matrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, matrix.get(x, y) == true ? 0xff000000 : 0xFFFFFFFF);
            }
        }
        return image;
    }
}
